/*  This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License,or (at
    your option) any later version.
    For more details, see the GNU General Public License (www.fsf.org or
    the COPYING file somewhere in the package)
 */

/*! @file src/launcher/Launcher.cc
    @brief OSGi launcher.
    @author @ref Guillaume_Terrissol
    @date 31st July 2014 - 12th October 2014
    @note This file is distributed under the GPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

#include <iostream>

#include <ApplicationService.hh>
#include <BundleMgr.hh>
#include <Context.hh>
#include <Utils.hh>

//------------------------------------------------------------------------------
//                           No-op Application Service
//------------------------------------------------------------------------------

namespace OSGi
{
    /*! @brief Default, and "empty", application service.
        It is provided so that the launcher successes, without doing anything, when run.
     */
    class DefaultApplicationService : public ApplicationService
    {
    public:
        //! @name Interface
        //@{
                    DefaultApplicationService();                                    //!< Constructor.
virtual             ~DefaultApplicationService();                                   //!< Destructor.

 static std::string name();                                                         //!< This service name.
        //@}
    private:

virtual int         process(const Args& pArgs, OSGi::Context::Ptr) override final;  //!< Application main function.
    };


    //! Defaulted.
    DefaultApplicationService::DefaultApplicationService() = default;

    //! Defaulted.
    DefaultApplicationService::~DefaultApplicationService() = default;

    /*! @return This service name : @b osgi.core.main
     */
    std::string DefaultApplicationService::name()
    {
        return "osgi.core.main";
    }

    /*! Displays the program arguments, and exits.
        @param pArgs    Application arguments
        @param pContext Bundle manager context
        @retval 0 (success)
     */
    int DefaultApplicationService::process(const Args& pArgs, OSGi::Context::Ptr pContext)
    {
        pContext->out() << "OSGi launch arguments :\n";

        for(auto a : pArgs)
        {
            pContext->out() << '\t' << a << '\t';
        }

        return 0;
    }
}

//------------------------------------------------------------------------------
//                                     Main
//------------------------------------------------------------------------------

/*! main() function.
    Arguments :
    - &ndash;&ndash;bundles=&lt;comma-separated bundles names&gt;,
    - &ndash;&ndash;main=&lt;name of application service to run&gt;,
    - &ndash;&ndash;version Displays OSGi version,
    - @ref OSGi_BundleMgr_Args_Page , prefixed with &ndash;&ndash;.
 */
int main(int pArgC, char* pArgV[])
{
    // OSGi default configuration.
    std::map<std::string, std::string>  lDefaultConfig =
    {
        { "bundleDir",    "../bundles" },
        { "cacheDir",     "cache" },
        { "tempDir",      "tmp" },
        { "permDir",      "sav" },
        { "configDir",    "cfg" },
    };
    // Parses arguments.
    const std::string   kOption  = "--";
    const std::string   kBundles = "--bundles=";
    const std::string   kMain    = "--main=";
    const std::string   kVersion = "--version";

    OSGi::ApplicationService::Args  lArgs{};
    std::string                     lService = OSGi::DefaultApplicationService::name();
    std::vector<std::string>        lBundles{};

    for(int a = 1; a < pArgC; ++a)
    {
        std::string lArgV{pArgV[a]};
        if      (lArgV.compare(0, kBundles.length(), kBundles) == 0)
        {
            auto    lNewBundles = OSGi::splitString(lArgV.substr(kBundles.length()), ',');
            std::copy(lNewBundles.begin(), lNewBundles.end(), std::back_inserter(lBundles));
        }
        else if (lArgV.compare(0, kMain.length(), kMain) == 0)
        {
            lService = lArgV.substr(kMain.length());
        }
        else if (lArgV.compare(0, kVersion.length(), kVersion) == 0)
        {
            std::cout << "OSGi-" << OSGI_VERSION << std::endl;
        }
        else if (lArgV.find(kOption) == 0)
        {
            auto    lOption  = lArgV.substr(kOption.length()); 
            auto    lEqual   = lOption.find('=');
            auto    lArg     = (lEqual != std::string::npos) ? lOption.substr(lEqual) : lOption; // arg[=value]
            auto    lDefault = lDefaultConfig.find(lArg);
            if (lDefault != lDefaultConfig.end())
            {
                lDefaultConfig.erase(lDefault); // Overwritten option.
                lArgs.push_back(lOption);
            }
        }
    }

    // Adds default options which haven't been overwritten.
    for(const auto& lDefault : lDefaultConfig)
    {
        lArgs.push_back(lDefault.first + "=" + lDefault.second);
    }

    // Loads what needs to be loaded.
    auto    lMgr = OSGi::BundleMgr::make(lArgs);
    lMgr->services()->checkIn("osgi.core.main", std::make_shared<OSGi::DefaultApplicationService>());
    lMgr->loadBundles();

    if (!lBundles.empty())
    {
        for(auto b : lBundles)
        {
            auto    lBndl = lMgr->context()->findBundle(b);
            lBndl->act(OSGi::kResolve, lMgr->context());
            lBndl->act(OSGi::kStart, lMgr->context());
        }
    }
    else
    {
        lMgr->startBundles();
    }

    // Here we go !
    return lMgr->services()->findByTypeAndName<OSGi::ApplicationService>(lService)->main(lArgs, lMgr->context());
}
